/* Turska Framework/UI library (Turs2)
 * Copyright 2003-2010 Jetro Lauha
 * All rights reserved.
 * Web: http://iki.fi/jetro/turska/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of EITHER:
 *   (1) The GNU Lesser General Public License as published by the Free
 *       Software Foundation; either version 2.1 of the License, or (at
 *       your option) any later version. The text of the GNU Lesser
 *       General Public License is included with this library in the
 *       file LICENSE-LGPL.txt.
 *   (2) The BSD-style license that is included with this library in
 *       the file LICENSE-BSD.txt.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
 * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
 *
 * $Id: T2DynamicArray.h 28 2012-07-28 17:03:06Z trzntaat $
 * $Revision: 28 $
 */

#ifndef T2DYNAMICARRAY_H_INCLUDED
#define T2DYNAMICARRAY_H_INCLUDED


#include "T2Types.h"


namespace turska
{

    class DynamicArrayImpl
    {
    public:
    
        DynamicArrayImpl(const I32 elementSize, I32 initialReserve = 8,
                         bool multiplySizeToGrow = true, I32 growAmount = 100);
        ~DynamicArrayImpl();
        I32 size() const;
        I32 getReservedSize() const;
        void getConfiguration(bool &multiplySizeToGrow, I32 &growAmount);
        bool reserve(I32 n);
        bool isEmpty();
        void clear();
        void * get(I32 index) const;
        void * getLast() const;
        bool set(I32 index, void *element);
        I32 find(void *element, I32 index = 0);
        bool add(void *element);
        bool insert(I32 index, void *element);
        void remove(I32 index);
        bool remove(I32 start, I32 end);
        void removeLast();
        void stdQSort(int (*compare)(const void *, const void *));
        I8 * getRawData() { return mData; }
    
    protected:
    
        const I32 mElementSize;
        I8 *mData;
        I32 mElementCount;
        I32 mReservedCount;
        I32 mGrowAmount;
        bool mMultiplySizeToGrow;
    
        void init(I32 initialReserve);
        bool growIfNoEmptySlots();
    
    private:
    
        // Copies of the array are not supported yet.
        DynamicArrayImpl(const DynamicArrayImpl &rhs);
        DynamicArrayImpl & operator=(const DynamicArrayImpl &rhs);
    };
    
    
    template<class T> class DynamicArray
    {
    public:
    
        /// Initializes the dynamic array.
        /** The initialReserve defines how many elements the array will initially
         *  be able to hold. When allocated amount needs to be grown, the
         *  multiplySizeToGrow defines if the previous size will be multiplied
         *  or just incremented. When multiplySizeToGrow is true, growAmount
         *  specified percentage of growth, that is, value of 100 means that
         *  the allocated size will be doubled. When multiplySizeToGrow is false,
         *  growAmount specifies directly amount of elements to allocate new
         *  space for. Minimum amount of new elements to allocate is always 1
         *  when allocation occurs.
         */
        DynamicArray(I32 initialReserve = 8, bool multiplySizeToGrow = true,
                     I32 growAmount = 100) :
            mImpl(sizeof(T), initialReserve, multiplySizeToGrow, growAmount) {}
    
        ~DynamicArray() {}
    
        /// Returns number of elements currently stored in the array.
        I32 size() const { return mImpl.size(); }
    
        /// Returns number of elements there is currently reserved storage for.
        I32 getReservedSize() const { return mImpl.getReservedSize(); }
    
        /// Writes current growing strategy configuration to given variables.
        void getConfiguration(bool &multiplySizeToGrow, I32 &growAmount)
        {
            mImpl.getConfiguration(multiplySizeToGrow, growAmount);
        }
    
        /// Reserves more space so that array can hold at least n elements.
        /** If n is less than current reserved size, no action is taken.
         *  Returns true on success and false if allocation failed.
         */
        bool reserve(I32 n) { return mImpl.reserve(n); }
    
        /// Returns true if no elements are stored in the array.
        bool isEmpty() { return mImpl.isEmpty(); }
    
        /// Discards all stored elements without discarding the allocated storage.
        void clear() { mImpl.clear(); }
    
        /// Returns const reference to an element in given index.
        const T & get(I32 index) const
        {
            return *((T *)mImpl.get(index));
        }
    
        /// Returns reference to an element in given index.
        /** The reference will become invalid when array is manipulated.
         */
        T & get(I32 index)
        {
            return *((T *)mImpl.get(index));
        }
    
        /// Returns const reference to last element.
        const T & getLast() const
        {
            return *((T *)mImpl.getLast());
        }
    
        /// Returns reference to last element.
        /** The reference will become invalid when array is manipulated.
         */
        T & getLast()
        {
            return *((T *)mImpl.getLast());
        }
    
        /// Returns const reference to an element in given index.
        const T & operator[](I32 index) const
        {
            return get(index);
        }
    
        /// Returns reference to an element in given index.
        /** The reference will become invalid when array is manipulated.
         */
        T & operator[](I32 index)
        {
            return get(index);
        }
    
        /// Sets contents of an element in given index.
        /** Index must be equal or less than current size. If index equals to
         *  current size, this is equivalent to add(element).
         *  Returns true when successful or false otherwise.
         */
        bool set(I32 index, const T &element)
        {
            return mImpl.set(index, (void *)&element);
        }
    
        /// Finds an element from the array.
        /** Returns the first index of the element occurring in the array, when
         *  searching forward from the given index (default is 0). Returns -1
         *  when no element was found.
         */
        I32 find(const T &element, I32 index = 0)
        {
            return mImpl.find((void *)&element, index);
        }
    
        /// Adds a new element to end of array. 
        /** Reallocation may occur.
         *  Returns true when successful or false otherwise.
         */
        bool add(const T &element)
        {
            return mImpl.add((void *)&element);
        }
    
        /// Inserts a new element to given index.
        /** Reallocation may occur.
         *  Returns true when successful or false otherwise.
         */
        bool insert(I32 index, const T &element)
        {
            return mImpl.insert(index, (void *)&element);
        }
    
        /// Removes an element from given index and returns it.
        T remove(I32 index)
        {
            T element = get(index);
            mImpl.remove(index);
            return element;
        }
    
        /// Removes range of elements with given indices.
        /** Start of range is inclusive, end of range is exclusive.
         * If start equals end, nothing is done and true is returned.
         * Returns true when successful or false otherwise.
         */
        bool remove(I32 start, I32 end)
        {
            return mImpl.remove(start, end);
        }
    
        /// Removes the last element and returns it.
        T removeLast()
        {
            T element = getLast();
            mImpl.removeLast();
            return element;
        }
    
        /// Sorts the array using standard C library qsort().
        void stdQSort(int (*compare)(const void *, const void *))
        {
            mImpl.stdQSort(compare);
        }
    
        T * getRawData() { return reinterpret_cast<T *>(mImpl.getRawData()); }

    protected:
    
        DynamicArrayImpl mImpl;
    
    private:
        
        // Copies of the array are not supported yet.
        DynamicArray(const DynamicArray &rhs);
        DynamicArray & operator=(const DynamicArray &rhs);
    };

} // namespace turska


#endif // !T2DYNAMICARRAY_H_INCLUDED
